import { ref, nextTick, onMounted, onBeforeUnmount, Ref } from "vue";
import { debounce } from "lodash-es";
interface DebouncedFunc<T extends (...args: any[]) => any> {
  /**
   * Call the original function, but applying the debounce rules.
   *
   * If the debounced function can be run immediately, this calls it and returns its return
   * value.
   *
   * Otherwise, it returns the return value of the last invocation, or undefined if the debounced
   * function was not invoked yet.
   */
  (...args: Parameters<T>): ReturnType<T> | undefined;

  /**
   * Throw away any pending invocation of the debounced function.
   */
  cancel(): void;

  /**
   * If there is a pending invocation of the debounced function, invoke it immediately and return
   * its return value.
   *
   * Otherwise, return the value from the last invocation, or undefined if the debounced function
   * was never invoked.
   */
  flush(): ReturnType<T> | undefined;
}
interface DebouncedFuncLeading<T extends (...args: any[]) => any>
  extends DebouncedFunc<T> {
  (...args: Parameters<T>): ReturnType<T>;
  flush(): ReturnType<T>;
}

interface Window {
  WebKitMutationObserver?: any;
  MozMutationObserver?: any;
}

export function observerDomResize(dom: HTMLElement, callback: any) {
  const MutationObserver =
    window.MutationObserver ||
    (window as Window).WebKitMutationObserver ||
    (window as Window).MozMutationObserver;

  const observer = new MutationObserver(callback);

  observer.observe(dom, {
    attributes: true,
    attributeFilter: ["style"],
    attributeOldValue: true,
  });

  return observer;
}

/**
 *
 * @param domRef 自适应尺寸的目标对象
 * @param onResize resize方法
 * @param afterAutoResizeMixinInit resize之后执行的回调函数
 */
export default function useAutoResize(
  domRef: Ref<HTMLElement | null>,
  onResize: any,
  afterAutoResizeMixinInit: any
) {
  const width = ref(0);
  const height = ref(0);
  const debounceInitWHFun = ref<any>(null);
  const domObserver = ref<any>(null);
  const initWH = async (resize = true) => {
    await nextTick(() => {
      width.value = domRef.value?.clientWidth || 0;
      height.value = domRef.value?.clientHeight || 0;
      console.log("zz", height.value, width.value);
      if (!domRef.value) {
        console.warn(
          "DataV: Failed to get dom node, component rendering may be abnormal!"
        );
      } else if (!width.value || !height.value) {
        console.warn(
          "DataV: Component width or height is 0px, rendering abnormality may occur!"
        );
      }
      if (typeof onResize === "function" && resize) onResize();
    });
  };
  const getDebounceInitWHFun = () => {
    debounceInitWHFun.value = debounce(initWH, 100);
  };
  const bindDomResizeCallback = () => {
    if (domRef.value) {
      domObserver.value = observerDomResize(
        domRef.value,
        debounceInitWHFun.value
      );
    }
    if (debounceInitWHFun.value) {
      window.addEventListener("resize", debounceInitWHFun.value as any);
    }
  };
  const autoResizeMixinInit = async () => {
    await initWH(false);

    getDebounceInitWHFun();

    bindDomResizeCallback();

    if (typeof afterAutoResizeMixinInit === "function")
      afterAutoResizeMixinInit();
  };
  const unbindDomResizeCallback = () => {
    if (!domObserver.value) return;

    domObserver.value.disconnect();
    domObserver.value.takeRecords();
    domObserver.value = null;

    window.removeEventListener("resize", debounceInitWHFun.value as any);
  };
  onMounted(() => {
    autoResizeMixinInit();
  });
  onBeforeUnmount(() => {
    unbindDomResizeCallback();
  });

  return {
    width,
    height,
  };
}
